/** https://sequelize.org/
 *  Sequelize是一个基于promise的Node.js ORM，具有强大的事务支持，关联关系，读取和复制等功能
 *    ORM：对象关系映射（把js中的对象与数据库进行关联，以对象的形式去操作数据库而不是sql语句）
 *    安装： npm i sequelize           -依赖于mysql2 需安装
 *    使用： const Sequelize = require('sequelize')
 *
 *  API:https://sequelize.org/master/identifiers.html
 *      C : class
 *      V : value
 *
 *  连接数据库
 *  const seq = new Sequlize('连接的数据库名'，'用户名'，'密码'，{
        其他选项：
        host：'127.0.0.1',          主机
        port:3306,                  端口
        dialect:'mysql',            要操作的数据库（ Postgres, MySQL, MariaDB, SQLite , Microsoft SQL Server.）
        timezone:'Asia/Shanghei'    写入的时区(https://www.php.net/manual/zh/timezones.php)
        ...
      })

     await seq.authenticate();   测试是否连接正常

    选择表：给每一个要操作的表定义一个对象 -模型Module 。 通过define定义      define(modelName: string, attributes: object, options: object)
    const Module = seq.define('表名称',{
        // 表中对应的字段信息，对象的key默认对应着表的column，字段
        字段名：{
          type: 字段类型(https://sequelize.org/master/variable/index.html#static-variable-DataTypes)
          allowNull：是否允许为空，默认为ture
          defaultValue：默认值，默认为null
          unique：值唯一，默认为false
          primaryKey：是否为主键，默认为false，
          field：数据库中字段的真实名称,
          autolncrement:是否自增，默认为false
        },{
          // 表中其他的选项
          timestameps:是否给每条记录添加createdAt和updatedAt字段，并在添加新数据和更新数据的时候自动设置这两个字段的值，默认为true
          paranoid：设置deletedAt字段，当删除一条记录的时候，并不是真的销毁记录，而是通过该字段来标识，即保留数据，进行假删除，默认为false
          freezeTableName：禁用修改表名。默认情况下，会将所有传递的模型名称(define的第一个参数)转换为复数，加上s。默认为false
          tableName:手动设置表的实际名称
          indexes:[               // 索引
            name: 索引名称
            field:['索引字段',...]
          ]
        }
      })

  模型类 -> 表
  模型创建出来的对象 -> 表中某条记录

    创建数据的方式： new Module()  或 通过Module.build() ，需要 模型对象.save()持久化保存
      let user1 = Module.build({
        // 表中字段和值
        name:"张三",
        age:18,
        gender:'男'
      })
      user1.save()

  (表数据)模型对象.save()    持久化到数据库中
        模型对象.set()     更改数据，但不会持久化到数据库中，需要手动调用save方法
        模型对象.update()  更新数据，等同于 set() + save() 方法
        模型对象.destory() 销毁改实例数据（假删除或真删除）

    查找数据 
      (表)模型类.findOne({ where:{id:1} })      // 查找一条记录 id为1
               .findOrCreate({where:{name:'黄蓉'}})    //  创建并查找 或 查找  name为黄蓉
               .findAll()            查找所有数据
               .findByPk(1)          查找主键为1的值
      limit、offset、order
        .findAll({
          limit:2       // 返回两条数据
          offset:1      // 偏移1条数据量
          order:[       // 排序 按年龄降序
            ['age','desc']
          ]
        })
    条件查询：Sequelize.Op
              .findAll({
                where:{
                  age:{
                    [Sequelize.Op.gt] : 10      // 查找年龄大于10的数据
                  }
                }
              })
      多条件：
            .findAll({                         // 查找年龄大于10 并且 性别为女
              [Sequelize.Op.or]:[
                {
                  age:{
                    [Sequelize.Op.gt] :10
                  }
                },{
                  gender:'女'
                }
              ]
            })
    
    统计
        (表)模型类.count()        // 返回表中所有数据的个数
                 .findAndCountAll({limit:2})     //  返回所有数据个数 和 符合条件的数据
                 .max('age')          // 返回最大值
                 .min('age')          // 返回最小值
                 .sum('age',{where:{gender:'女'}})    // 返回 总和


    关联查询和预加载：  
    hasOne():             model1.HasOne(model2)                 模型1有模型2      一对一         用户表有评论表
    hasMany():            model1.HasMany(model2)                模型1有模型2      一对多
    belongsTo():          model1.BelongsTo(model2)              模型1属于模型2    一对一         评论表属于用户表 
    belongsToMany():      model1.BelongsToMany(model2)          模型1属于模型2    一对多
    (1).首先给关联的字段定义外键关系
          references:{
            model:关联的外键表
            key:关联的外键表的字段
          }
    (2).在调用HasOne()或HasMany()等方法的时候，通过第二个参数设置对象
          { foreignKey:当前关联表的字段 }
 *
 */